#ifndef __tenacitas_log_core_file_tester_h__
#define __tenacitas_log_core_file_tester_h__

#include <iostream>
#include <cstdint>
#include <string>

#include "runtest.h"

#include <tenacitas.return_code.core/rc.h>
#include <tenacitas.log.core/file.h>
#include <tenacitas.log.core/thread_id.h>
#include <tenacitas.log.core/level.h>

namespace tenacitas {
    namespace log {
        namespace core {

            class file_tester {
            private:
                struct supplier {
                    struct writer {

                        /** */
                        static const class_id id = 14347242877764ULL;

                        enum constants : constant_id {
                            could_not_open_file = 1,
                                error_closing_file = 2,
                                error_writing_to_file = 3
                                };
                                                
                        rc<std::unique_ptr<std::ofstream>>
                        open_file(const std::string & p_name) noexcept {
                            try {
                                std::unique_ptr<std::ofstream>
                                    _file(new std::ofstream(p_name,
                                                            std::ios::trunc));
                                return
                                    rc<std::unique_ptr<std::ofstream>>
                                    (std::move(_file));  
                            } catch (std::exception & _ex) {
                                return rc<std::ofstream>(
                                    id,
                                    constants::could_not_open_file,
                                    __FILE__, __LINE__,
                                    p_name.c_str());
                            }
                        }

                        rc<> close_file(std::ofstream & p_file) noexcept {
                            p_file.close();
                            if (!p_file.good()) {
                                return rc<>(id, constants::error_closing_file,
                                            __FILE__, __LINE__);
                            }
                            return rc<>();
                        }

                        rc<> new_line(std::ofstream & p_file,
                                      uint64_t p_timestamp,
                                      thread_id p_thread_id,
                                      level p_level,
                                      const char * p_file_name,
                                      int64_t p_line) noexcept {
                            p_file << p_level << "|"
                                   << p_timestamp << "|"
                                   << p_thread_id << "|"
                                   << p_file_name << "|"
                                   << p_line << "|";

                            if (!p_file.good()) {
                                return rc<>(id, constants::error_writing_to_file,
                                            __FILE__, __LINE__);
                            }
                            return rc<>();

                        }

                        template<typename data>
                        rc<>  write (std::ofstream & p_file, const data & p_data) 
                            noexcept { 
                            p_file << p_data;
                            if (!p_file.good()) {
                                return rc<>(id, constants::error_writing_to_file,
                                            __FILE__, __LINE__);
                            }
                            return rc<>();
                        }

                        template<typename data>
                        rc<>  write (std::ofstream & p_file, data && p_data)
                            noexcept { 
                            p_file << p_data;
                            if (!p_file.good()) {
                                return rc<>(id, constants::error_writing_to_file,
                                            __FILE__, __LINE__);
                            }
                            return rc<>();
                        }

                        writer(writer &&) {}

                        writer(){}

                        writer & operator=(writer &&) {
                            return *this;
                        }
                        
                    };                    

                    struct change_file_notifier {
                        rc<bool> change_now(std::uint64_t p_size) {
                            return rc<bool>(false);
                        }

                        change_file_notifier(change_file_notifier &&){}

                        change_file_notifier &
                        operator=(change_file_notifier&&){
                            return *this;
                        }

                        change_file_notifier(){}
                        
                    };

                    struct change_file_handler {

                        change_file_handler(thread_id p_th_id) {}

                        change_file_handler() {}

                        change_file_handler(change_file_handler &&) {}

                        change_file_handler &
                        operator =(change_file_handler &&) {
                            return *this;
                        }
                        

                        rc<> file_closed() {
                            return rc<>();
                        }


                        rc<std::string> file_name() noexcept {
                            return rc<std::string>("logfile.log");
                        }

                        rc<> file_changed() noexcept {
                            return rc<>();
                        }
                    };
                    
                };

                typedef core::file<supplier> file;
                

                static bool test1() {
                    std::cerr << "\n+++ file_tester::test1" << std::endl;

                    try {
                        file _file(thread_id());
                        return true;
                    } catch (std::exception & _ex) {
                        std::cerr << _ex.what() << std::endl;
                    }
                    return false;
                }

                static bool test2() {
                    std::cerr << "\n+++ file_tester::test2" << std::endl;

                    try {
                        thread_id _th;
                        
                        file _file1(_th);

                        file _file2(std::move(_file1));

                        return true;
                    } catch (std::exception & _ex) {
                        std::cerr << _ex.what() << std::endl;
                    }
                    return false;
                }
                
                static bool test3() {
                    std::cerr << "\n+++ file_tester::test3" << std::endl;

                    try {
                        thread_id _th;
                        
                        file _file1(_th);

                        file _file2(std::move(_file1));

                        return true;
                    } catch (std::exception & _ex) {
                        std::cerr << _ex.what() << std::endl;
                    }
                    return false;
                }


            public:
                static void run() {
                    runtest(file_tester::test1());
                    runtest(file_tester::test2());
                }

            };


            const class_id file_tester::supplier::writer::id;

        }
    }
}


#endif
